Explore struct vs class vs actor differences in this in-depth article.
| Structs | Classes | Actors | |
|---|---|---|---|
| Type | Value type |
Reference type |
Reference type |
| Inheritance | ❌ |
✅ |
❌ |
| Thread−safety | ✅ |
❌ |
✅ |
| Memberwise Initializer |
✅ |
❌ |
❌ |
| Deinitializer | ❌ |
✅ |
✅ |
| Memory allocation | Stack |
Heap |
Heap |
| External access to public properties and methods |
direct |
direct, but by using `await` |
|
| Protocols |
|
|
|
| Method execution | Can potentially be executing severals methods at a time |
Can execute only one method at a time |
|
Other |
Can:
|
||
- It's a value type (along with e.g. arrays, dictionaries, enums and tuples).
- Every time you use the struct reference in your code, it is potentially a new struct.
- But Swift copy that new struct to a new memory address only if you modify the struct.
- It called copy-on-write pattern. It's a memory optimization.
- Every time you use the struct reference in your code, it is potentially a new struct.
- Structs cannot cause memory leaks.
- Apple recommendation: use structures by default for storing data and modeling behavior.
- When you don't need class' features:
- You don't need to control the object's identity (use the identity operator
===). - You don't need a shared mutable state feature.
- You don't need Objective-C interoperability.
- You don't need class inheritance.
- You need to replace class inheritance:
- Protocols and structures can replace class inheritance.
- You don't need to control the object's identity (use the identity operator
- It's a reference type.
- Classes can cause memory leaks.
- You need to control the object's identity (use the identity operator
===). - You need a shared mutable state feature.
- You need Objective-C interoperability.
- You need class inheritance.
- It's a reference type.
- Actors can cause memory leaks.
- You need to use a reference type.
- You need thread safety for shared mutable state.
- It eliminates all kinds of race conditions and data races.
- For your SwiftUI data models:
- Instead use a class that conforms to the
ObservableObjectprotocol.- If needed, you can optionally also mark that class with
@MainActorto ensure it does any UI work safely, - But keep in mind that using
@StateObject/@ObservedObjectautomatically makes a view’s code run on the main actor.
- If needed, you can optionally also mark that class with
- If you desperately need to be able to carve off some async work safely:
- Create a sibling actor – a separate actor that does not use
@MainActor, but does not directly update the UI.
- Create a sibling actor – a separate actor that does not use
- Instead use a class that conforms to the